import template from "lodash.template";
import fs from "node:fs";
import path from "node:path";
import prettier from "prettier";
import { rimraf } from "rimraf";
import {
	registrySchema,
	type Registry,
	type RegistryItem,
	type RegistryItemType,
} from "@shadcn-svelte/registry";
import { generateBaseColorTemplate, getColorsData } from "../src/lib/components/colors/colors.js";
import { buildRegistry } from "./registry.js";
import { baseColors } from "../src/lib/registry/registry-colors.js";
import { THEME_STYLES_WITH_VARIABLES } from "../src/lib/registry/templates.js";
import { baseColorsOKLCH } from "../src/lib/registry/registry-base-colors.js";

const prettierConfig = await prettier.resolveConfig(import.meta.url);
if (!prettierConfig) throw new Error("Failed to resolve prettier config.");

const REGISTRY_PATH = path.resolve("static", "registry");
const THEMES_CSS_PATH = path.resolve("static");

function writeFileWithDirs(
	filePath: string,
	data: string,
	options: Parameters<typeof fs.writeFileSync>[2] = {}
): void {
	// Create directory path if it doesn't exist
	const dirname = path.dirname(filePath);
	fs.mkdirSync(dirname, { recursive: true });

	// Write the file
	fs.writeFileSync(filePath, data, options);
}

export async function build(): Promise<void> {
	const registry = await buildRegistry();

	const selfReferenced = registry.filter((item) => item.registryDependencies.includes(item.name));
	const selfReferenceError = selfReferenced
		.map((item) => `Registry item '${item.name}' depends on itself`)
		.join("\n");
	if (selfReferenceError) {
		throw new Error(selfReferenceError);
	}

	const initItem: RegistryItem = {
		name: "init",
		type: "registry:style",
		devDependencies: ["tailwind-variants", "@lucide/svelte", "tw-animate-css"],
		registryDependencies: ["utils"],
		files: [],
	};

	// ----------------------------------------------------------------------------
	// Build `registry.json` file.
	// ----------------------------------------------------------------------------
	const result = registrySchema.parse(
		{
			$schema: "./static/schema/registry.json",
			name: "shadcn-svelte",
			homepage: "https://shadcn-svelte.com",
			aliases: {
				lib: "$lib/registry/lib",
				ui: "$lib/registry/ui",
				components: "./components",
				hooks: "$lib/registry/hooks",
				utils: "$lib/utils",
			},
			// TODO: remove when moving from `next` to `latest`
			overrideDependencies: ["paneforge@next", "vaul-svelte@next"],
			items: [initItem, ...registry],
		} as Registry,
		// maintains the schema defined property order
		{ jitless: true }
	);

	const ITEM_TYPES: RegistryItemType[] = [
		"registry:ui",
		"registry:hook",
		"registry:style",
		"registry:lib",
		"registry:block",
	];
	const filteredItems = result.items.filter((item) => ITEM_TYPES.includes(item.type));
	const registryJsonPath = path.resolve("registry.json");

	const registryJson = JSON.stringify({ ...result, items: filteredItems }, null, "\t");
	const formatted = await prettier.format(registryJson, {
		...prettierConfig,
		filepath: registryJsonPath,
	});
	fs.writeFileSync(registryJsonPath, formatted, "utf8");

	// ----------------------------------------------------------------------------
	// Build __registry__/blocks.ts
	// ----------------------------------------------------------------------------
	rimraf.sync(path.resolve("src", "__registry__"));

	let blocksIndex = `
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
export const blocks = [
`; // Creates block index files
	for (const block of result.items) {
		if (block.type !== "registry:block" || block.name.startsWith("chart-")) continue;

		blocksIndex += `"${block.name}",`;
	}

	blocksIndex += "\n] as const;\n";
	const blocksPath = path.resolve("src", "__registry__", "blocks.ts");
	writeFileWithDirs(blocksPath, blocksIndex);

	// ----------------------------------------------------------------------------
	// Build __registry__/index.js.
	// ----------------------------------------------------------------------------
	let index = `
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
export const Index = {`;

	// Build style index.
	for (const item of result.items) {
		if (item.type !== "registry:example") continue;
		const resolveFiles = item.files.map((file) => file.path.replace("src/", "../"));

		index += `
"${item.name}": {
	files: [${resolveFiles.map((file) => `"${file.replaceAll(path.sep, "/")}"`)}],
},`;
	}

	index += `
}
`;

	// Write style index.
	const registryPath = path.resolve("src", "__registry__", "index.js");
	rimraf.sync(registryPath);
	writeFileWithDirs(registryPath, index);

	// ----------------------------------------------------------------------------
	// Build registry/colors/index.json.
	// ----------------------------------------------------------------------------
	const colorsTargetPath = path.join(REGISTRY_PATH, "colors");
	rimraf.sync(colorsTargetPath);
	if (!fs.existsSync(colorsTargetPath)) {
		fs.mkdirSync(colorsTargetPath, { recursive: true });
	}

	const colorsData = getColorsData();

	writeFileWithDirs(
		path.join(colorsTargetPath, "index.json"),
		JSON.stringify(colorsData, null, "\t"),
		"utf-8"
	);

	// ----------------------------------------------------------------------------
	// Build registry/colors/[base].json.
	// ----------------------------------------------------------------------------

	const themeCSS = [];
	for (const baseColor of baseColors) {
		const base = generateBaseColorTemplate(baseColor);
		const zincCssVars = generateBaseColorTemplate("zinc");

		themeCSS.push(
			template(THEME_STYLES_WITH_VARIABLES)({
				colors: {
					...zincCssVars.cssVars,
					...baseColorsOKLCH[baseColor as keyof typeof baseColorsOKLCH],
				},
				theme: baseColor,
			})
		);

		if (["zinc", "stone", "slate", "gray", "neutral"].includes(baseColor)) {
			writeFileWithDirs(
				path.join(REGISTRY_PATH, "colors", `${baseColor}.json`),
				JSON.stringify(base, null, "\t"),
				"utf-8"
			);
		}
	}

	// ----------------------------------------------------------------------------
	// Build registry/themes.css
	// ----------------------------------------------------------------------------

	writeFileWithDirs(path.join(THEMES_CSS_PATH, `themes.css`), themeCSS.join("\n\n"), "utf-8");
}

if (process.argv.includes("build-registry")) {
	build();
}
